#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <semaphore.h>
#include <pthread.h>

using namespace std;

template <class T>
class RingQueue
{
private:
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void Unlock(pthread_mutex_t &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }

public:
    RingQueue(int cap) : _cap(cap), _prostep(0), _constep(0)
    {
        sem_init(&_roomsem, 0, _cap);
        sem_init(&_datasem, 0, 0);

        pthread_mutex_init(&_promutex, nullptr);
        pthread_mutex_init(&_conmutex, nullptr);
    }

    void Enqueue(const T &in)
    {
        P(_roomsem);
        Lock(_promutex);
        _ringqueue[_prostep++] = in;
        _prostep %= _cap;
        Unlock(_promutex);
        V(_datasem);
    }
    void Pop(T *out)
    {
        P(_datasem);
        Lock(_conmutex);
        *out = _ringqueue[_constep++];
        _constep %= _cap;
        Unlock(_conmutex);
        V(_roomsem);
    }
    ~RingQueue()
    {
        sem_destroy(&_roomsem);
        sem_destroy(&_datasem);

        pthread_mutex_destroy(&_promutex);
        pthread_mutex_destroy(&_conmutex);
    }

private:
    vector<T> _ringqueue;
    int _cap;

    int _prostep;
    int _constep;

    sem_t _roomsem; // 生产
    sem_t _datasem; // 消费

    pthread_mutex_t _promutex;
    pthread_mutex_t _conmutex;
};